<?php
require_once "QH.Config.Interface.php";

/**
 *
 * 数据对象基础类，该类中定义数据类最基本的行为，包括：
 * 计算/设置/获取签名、输出json格式的参数、从json读取数据对象等
 * @author Zhang Xingliang
 *
 */
class QHBaseEntity
{
    protected $values = array();

    /**
     *
     * 设置参数
     * @param string $key
     * @param string $value
     */
    public function setData($key, $value)
    {
        $this->values[$key] = $value;
    }

    /**
     * 判断参数是否存在为空
     * @return true 或 false
     **/
    public function IsEmpty($key)
    {
        return !(array_key_exists($key, $this->values) && isset($this->values[$key]));
    }

    /**
     * 设置签名，详见签名生成算法类型
     * @param string $value
     **/
    public function setSignType($sign_type)
    {
        $this->values['sign_type'] = $sign_type;
        return $sign_type;
    }

    /**
     * 设置签名，详见签名生成算法
     * @param string $value
     **/
    public function setSign($config)
    {
        $sign = $this->sign($config);
        $this->values['sign'] = $sign;
        return $sign;
    }

    /**
     * 获取签名，详见签名生成算法的值
     * @return 值
     **/
    public function getSign()
    {
        return $this->values['sign'];
    }

    /**
     * 检测签名
     * @param QHConfigInterface $config 配置对象
     * @throws QHException
     */
    public function verifySign($config)
    {
        if ($this->IsEmpty('sign')) {
            QHException::ThrowQHException("签名错误！");
        }

        $sign = $this->sign($config, false);

        if ($this->getSign() == $sign) {
            //签名正确
            return true;
        }
        QHException::ThrowQHException("签名错误！");
    }

    /**
     * 设置应用ID
     * @param string $value
     **/
    public function setAppId($value)
    {
        $this->values['app_key'] = $value;
    }

    /**
     * 获取应用ID的值
     * @return 值
     **/
    public function getAppId()
    {
        return $this->values['app_key'];
    }

    /**
     * 设置时间戳，详见签名生成算法
     * @param string $value
     **/
    public function setTimeStamp($value)
    {
        $this->values['time_stamp'] = $value;
    }

    /**
     * 获取时间戳
     * @return string $time_stamp
     **/
    public function getTimeStamp()
    {
        return $this->values['time_stamp'];
    }

    /**
     * 随机字符串
     * @param string $value
     **/
    public function setNonceStr($value)
    {
        $this->values['nonce_str'] = $value;
    }

    /**
     * 获取随机字符串
     * @return string $nonce_str
     **/
    public function getNonceStr()
    {
        return $this->values['nonce_str'];
    }

    /**
     * 输出JSON字符
     * @return string $jsonStr
     **@throws QHException
     */
    public function toJSONString()
    {
        if (!is_array($this->values) || count($this->values) <= 0) {
            QHException::ThrowQHException("数组数据异常！");
        }
        return json_encode($this->values);
    }

    /**
     * 将xml转为array
     * @param string $jsonStr
     * @return array $values
     * @throws QHException
     */
    public function fromJson($jsonStr)
    {
        if (!$jsonStr) {
            QHException::ThrowQHException("json数据异常！");
        }
        $this->values = json_decode($jsonStr, true);
        return $this->values;
    }

    /**
     * 格式化参数格式化成url参数
     * @return string
     */
    public function toUrlParams()
    {
        $buff = "";
        foreach ($this->values as $k => $v) {
            if ($k != "sign" && $v != "" && !is_array($v)) {
                $buff .= $k . "=" . $v . "&";
            }
        }

        $buff = trim($buff, "&");
        return $buff;
    }

    /**
     * 生成签名
     * @param QHConfigInterface $config 配置对象
     * @param bool $needSignType 是否需要补signtype
     * @return string 签名，本函数不覆盖sign成员变量，如要设置签名需要调用SetSign方法赋值
     * @throws QHException
     */
    public function sign($config, $needSignType = false)
    {
        if ($needSignType) {
            $this->setSignType($config->getSignType());
        }
        //签名步骤一：按字典序排序参数
        ksort($this->values);
        $string = $this->toUrlParams();
        //签名步骤二：在string后加入KEY
        $string = $string . "&app_secret=" . $config->getAppSecret();
        //签名步骤三：MD5加密或者HMAC-SHA256
        if ($config->getSignType() == "MD5") {
            $string = md5($string);
        } else if ($config->getSignType() == "HMAC-SHA256") {
            $string = hash_hmac("sha256", $string, $config->getAppSecret());
        } else {
            QHException::ThrowQHException("签名类型不支持！");
        }
        //签名步骤四：所有字符转为大写
        return strtoupper($string);
    }

    /**
     * 获取设置的值
     */
    public function getValues()
    {
        return $this->values;
    }
}

/**
 *
 * 接口调用结果类
 * @author Zhang Xingliang
 *
 */
class QHResults extends QHBaseEntity
{
    /**
     * 将json转为array
     * @param QHConfigInterface $config 配置对象
     * @param string $data
     * @return array
     * @throws QHException
     */
    public static function Init($config, $data, $noVerifySign = false)
    {
        $obj = new self();
        $obj->fromJson($data);
        //失败则直接返回失败
        if ($obj->values['code'] != 200) {
            foreach ($obj->values as $key => $value) {
                #除了code和msg之外其他的参数存在，则报错
                if ($key != "msg" && $key != "code") {
                    QHException::ThrowQHException("输入数据存在异常！");
                }
            }
            return $obj->getValues();
        }
        if ($noVerifySign == false) {
            $obj->verifySign($config);
        }
        return $obj->getValues();
    }
}

/**
 *
 * 回调基础类
 * @author Zhang Xingliang
 *
 */
class QHNotifyReply extends QHBaseEntity
{
    /**
     *
     * 设置错误码 FAIL -1 或者 SUCCESS 200
     * @param string
     */
    public function setCode($code)
    {
        $this->values['code'] = $code;
    }

    /**
     *
     * 获取错误码 FAIL -1 或者 SUCCESS 200
     * @return string $code
     */
    public function getCode()
    {
        return $this->values['code'];
    }

    /**
     *
     * 设置错误信息
     * @param string $msg
     */
    public function setMsg($msg)
    {
        $this->values['msg'] = $msg;
    }

    /**
     *
     * 获取错误信息
     * @return string
     */
    public function getMsg()
    {
        return $this->values['msg'];
    }

}

